﻿using System;
using System.Collections.Generic;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using Inovout.Gis;
using Inovout.Gis.Geometry;

namespace Inovout.ArcGis
{
    public abstract class GeometryInfo : IGeometryInfo
    {
        #region Constructor
     
        private ArcGisRow row;
        protected GeometryInfo(ArcGisRow row)
            :this(row.ArcFeature.Shape)
        {
            this.row = row;
        }

        private IGeometry arcGeometry;
        protected GeometryInfo(IGeometry arcGeometry)
        {
            this.arcGeometry = arcGeometry;
        }

        #endregion

        public double EllipsoidArea
        {
            get { return 0; }
        }


        #region Row

        public IGisRow Row
        {
            get { return row; }
        }

        #endregion

        #region Layer

        private IGeometryLayerInfo layer;
        public IGeometryLayerInfo Layer
        {
            get { return layer; }
            internal set { layer = value; }
        }

        #endregion

        #region Area
        public double Area
        {
            get
            {
                return CalculateArea();
            }
        }
        protected virtual double CalculateAreaFromGeometry()
        {
            return 0;
        }

        protected readonly string AreaFieldName = "Area";
        protected virtual double CalculateAreaFromFeature()
        {
            double area;
            row.TryGetValue<double>(AreaFieldName, out area);
            return area;
        }
        private double CalculateArea()
        {
            //先从冗余的Feature表中获取
            double area = 0;
            if (row != null)
            {
                area = CalculateAreaFromFeature();
            }
            //Feature不存在或者为零，再通过图形计算
            if (area == 0)
            {
                area = CalculateAreaFromGeometry();
            }
            return area;
        }

        #endregion

        #region Shape
        public object Shape
        {
            get { return arcGeometry; }
        }

        #endregion

        #region RelationId

        public object RelationId
        {
            get { return GetRelationId(); }
        }
        protected virtual object GetRelationId()
        {
            object relationId;
            row.TryGetValue(RelationIdFieldName, out relationId);
            return relationId;
        }
        protected readonly string RelationIdFieldName = "RelationId";
        #endregion


        #region Intersect
       
        public virtual IGeometryInfo GetIntersectGeometry(IGeometryInfo otherGeometryInfo)
        {        
             //很有必要增加冗余字段，最起码便于指定区域查询
            ITopologicalOperator topologicalOperator = arcGeometry as ITopologicalOperator;

            IGeometry intersectedGeometry = topologicalOperator.Intersect(otherGeometryInfo.Shape as IGeometry, GetGeoetryDimension(otherGeometryInfo));
            if (intersectedGeometry == null || intersectedGeometry.IsEmpty)
            {
                return null;
            }
            else
            {
                return ArcGisHelper.CreateArcGeometry(intersectedGeometry);
            }
        }

        private esriGeometryDimension GetGeoetryDimension(IGeometryInfo geometry)
        {
            if (geometry is IPointInfo)
            {
                return esriGeometryDimension.esriGeometry0Dimension;

            }
            else if (geometry is ILineInfo)
            {
                return esriGeometryDimension.esriGeometry1Dimension;
            }
            else if (geometry is IPolygonInfo)
            {
                return esriGeometryDimension.esriGeometry2Dimension;
            }
            else
            {
                throw new NotImplementedException(geometry.GetType().ToString());
            }
        }

        public bool IsEnvelopeIntersected(IGeometryInfo otherGeometryInfo)
        {
            //?需比较读库和读图的性能
            IEnvelope otherEnvelope = ((IGeometry)otherGeometryInfo.Shape).Envelope;

            //左上角(minX,maxY)、右上角(maxX,maxY)，左下角(minX,MinY)，右下角(maxX,minY)
            return (arcGeometry.Envelope.XMin < otherEnvelope.XMin & arcGeometry.Envelope.YMax > otherEnvelope.YMax) |
                   (arcGeometry.Envelope.XMax > otherEnvelope.XMax & arcGeometry.Envelope.YMax > otherEnvelope.YMax) |
                   (arcGeometry.Envelope.XMin < otherEnvelope.XMin & arcGeometry.Envelope.YMin < otherEnvelope.YMin) |
                   (arcGeometry.Envelope.XMax > otherEnvelope.XMax & arcGeometry.Envelope.YMin < otherEnvelope.YMin);
        }

        public double EnvelopeMinX 
        {
            get
            {
                if (row.Table.HasField(EnvelopeMinXFieldName))
                {
                    return row.GetValue<double>(EnvelopeMinXFieldName);
                }
                else
                {
                    return arcGeometry.Envelope.XMin;
                }
            }
        }
        public double EnvelopeMaxX
        {
            get
            {
                if (row.Table.HasField(EnvelopeMinXFieldName))
                {
                    return row.GetValue<double>(EnvelopeMinXFieldName);
                }
                else
                {
                    return arcGeometry.Envelope.XMin;
                }
            }
        }
        public double EnvelopeMinY
        {
            get
            {
                if (row.Table.HasField(EnvelopeMinXFieldName))
                {
                    return row.GetValue<double>(EnvelopeMinXFieldName);
                }
                else
                {
                    return arcGeometry.Envelope.XMin;
                }
            }
        }
        public double EnvelopeMaxY
        {
            get
            {
                if (row.Table.HasField(EnvelopeMinXFieldName))
                {
                    return row.GetValue<double>(EnvelopeMinXFieldName);
                }
                else
                {
                    return arcGeometry.Envelope.XMin;
                }
            }
        }
        internal static readonly string EnvelopeMinXFieldName = "MinX";
        internal static readonly string EnvelopeMinYFieldName = "MinY";
        internal static readonly string EnvelopeMaxXFieldName = "MaxX";
        internal static readonly string EnvelopeMaxYFieldName = "MaxY";
        public List<IGeometryInfo> GetIntersectedGeometries(params string[] layerNames)
        {
            List<IGeometryInfo> intersectecdGeometries = new List<IGeometryInfo>();
            ArcGisInfoRepository gisInfoRepository = new ArcGisInfoRepository(row.Table.DataSource as IWorkspace);
            if (row.Table.HasField(EnvelopeMinXFieldName))
            {
                foreach (string layerName in layerNames)
                {
                    List<IGeometryInfo> intersectedRepositoryGeometries = gisInfoRepository.FindIntersectedGeometriesByEnvelope(layerName,arcGeometry);
                    foreach (var geometry in intersectedRepositoryGeometries)
                    {
                        intersectecdGeometries.Add(this.GetIntersectGeometry(geometry));
                    }
                }
            }
            else
            {
                foreach (string layerName in layerNames)
                {
                    intersectecdGeometries.AddRange(this.GetIntersectedGeometries(gisInfoRepository.GetGeometryLayer(layerName)));
                }
            }
            return intersectecdGeometries;
        }

        public List<IGeometryInfo> GetIntersectedGeometries(params IGeometryLayerInfo[] layers)
        {
            List<IGeometryInfo> intersectecdGeometriers = new List<IGeometryInfo>();
            foreach (var layer in layers)
            {
                foreach (var geometry in layer.Geometries)
                {
                    IGeometryInfo intersectedGeometry = GetIntersectGeometry(geometry);

                    if (intersectedGeometry != null)
                    {
                        intersectecdGeometriers.Add(intersectedGeometry);
                    }
                }
            }
            return intersectecdGeometriers;
        }
        #endregion

        #region GeometryId

        public object GeometryId
        {
            get { return row.GeometryId; }
        }

        #endregion



    }
}
